﻿using System;
using Microsoft.Linq.Expressions;
using Microsoft.Scripting.Actions;
using Microsoft.Scripting.Utils;

namespace Cjc.ExpressionEngine.Excel.Runtime.Binding
{
	public class OperationBinder : OperationAction
	{
		public ExcelBinder Binder { get; private set; }
		public Expression Context { get; private set; }

		public OperationBinder( ExcelBinder binder, Expression context, string operation )
			: base( operation )
		{
			this.Binder = binder;
			this.Context = context;
		}

		public override MetaObject Fallback( MetaObject target, MetaObject[] args, MetaObject onBindingError )
		{
			var op = Operation;

			switch ( op )
			{
				case "Concatenate":
					{
						target = EnsureString( target );
						op = "Add";
						break;
					}

				case "Add":
				case "Subtract":
				case "Multiply":
				case "Divide":
					{
						target = EnsureNumeric( target );
						args[ 0 ] = EnsureNumeric( args[ 0 ] );
						break;
					}
			}

			var result = Binder.DoOperation( op, Context, ArrayUtils.Insert<MetaObject>( target, args ) );

			return result;
		}

		private MetaObject EnsureString( MetaObject t )
		{
			return ( t.RuntimeType != typeof( string ) )
				? Binder.ConvertTo( typeof( string ), ConversionResultKind.ImplicitCast, t )
				: t;
		}

		private MetaObject EnsureNumeric( MetaObject t )
		{
			switch ( Type.GetTypeCode( t.RuntimeType ) )
			{
				case TypeCode.Int32:
				case TypeCode.Int64:
				case TypeCode.Double:
				case TypeCode.Decimal:
					return t;

				default:
					return Binder.ConvertTo( typeof( decimal ), ConversionResultKind.ImplicitCast, t );
			}
		}

		public override object HashCookie
		{
			get { return this; }
		}
	}
}